#ifndef OBJECTPOOLMANAGER_H
#define OBJECTPOOLMANAGER_H
#include "MemoryBlock.h"
#include "ObjectPool.h"
#include "StompedReality/Core.h"
#include "StompedReality/Core/Component.h"
#include "StompedReality/Core/Delegate.h"
#include "StompedReality/Core/GameAccess.h"
#include <assert.h>
#include <map>
namespace StompedReality
{
namespace Core
{
/*!
 * \author	Stefan Kazalski
 *
 * \brief	Object manager
 *
 *    Manages objects of base type T.<br>
 */
template <class T>
class DLL_EXPORT ObjectPoolManager : public StompedReality::GameAccess
{
 private:

  typedef std::map<TYPE_ID, MemoryBlock<T>* > IDBlockMap;
  IDBlockMap _blockMap;

 public:

  //! \brief	Constructor.
  ObjectPoolManager( );

  //! \brief	Destructor.
  virtual ~ObjectPoolManager( );

  //! \brief	Adds a memory block.
  //! \param	type	The memory blocks type.
  //! \param  block	The memory block.
  void AddObjectPool ( TYPE_ID type, MemoryBlock<T>* block );

  //! \brief	Creates a memroy block.
  //! \param	info	The type info.
  //! \param	capacity	The capacity.
  void CreateBlock ( StompedReality::type_system::TypeInfo* info, int capacity );

  //! \brief	Creates and object of the type.
  //! \param	type	The type.
  //! \return The objects id.
  ID CreateObject ( TYPE_ID type );

  //! \brief	Does object exist?
  //! \param	type	The type.
  //! \param	ID		The objects id.
  //! \return True if exists otherwise false.
  bool HasObject ( TYPE_ID type, ID ID );

  //! \brief Object getter.
  //! \param	type	The type.
  //! \param	ID		The objects id.
  //! \return	The object if exits otherwise false.
  T* GetObject ( TYPE_ID type, ID ID );

  //! \brief	Parses configuration.
  //! \param	field	The nodes name.
  //!	\param	root	The json node.
  //! \return Sucess.
  bool ParseConfig ( JsonPTR root );

  MemoryBlock<T>* GetMemoryBlock ( TYPE_ID type );

  template <typename OBJECT_TYPE, typename RETURN_TYPE>
  std::function<RETURN_TYPE ( )> CreateFunctionDelegate( StompedReality::TYPE_ID type, StompedReality::ID id,
                                                         RETURN_TYPE ( OBJECT_TYPE::* FN )( ) )
  {
    OBJECT_TYPE* cmp = ( OBJECT_TYPE* ) this->GetObject( type, id );
    return StompedReality::CreateDelegate( cmp, FN );
  };

  template <typename RETURN_TYPE>
  std::function<RETURN_TYPE ( )> CreateGetterDelegate( StompedReality::TYPE_ID type, StompedReality::ID id,
                                                       const char* name )
  {
    return ((ObjectPool< T >*) this->_blockMap[type])->CreateGetterDelegate<RETURN_TYPE>( id, name );
  };

  template <typename INPUT_TYPE>
  std::function<void ( INPUT_TYPE )> CreateSetterDelegate( StompedReality::TYPE_ID type, StompedReality::ID id,
                                                           const char* name )
  {
    return ((ObjectPool< T >*) this->_blockMap[type])->CreateSetterDelegate<INPUT_TYPE>( id, name );
  };

  template <typename TYPE>
  StompedReality::ValueDelegate<TYPE> CreateValueDelegate( StompedReality::TYPE_ID type, StompedReality::ID id,
                                                           const char* name )
  {
    return ((ObjectPool< T >*) this->_blockMap[type])->CreateValueDelegate<TYPE>( id, name );
  };

};

template <class T>
ObjectPoolManager<T>::ObjectPoolManager( ) : StompedReality::GameAccess( )
{
}

template <class T>
ObjectPoolManager<T>::~ObjectPoolManager( )
{
  LogCallFunctionInfo( "ObjectPoolManager", "Destructor" );

  for ( typename IDBlockMap::iterator iter = this->_blockMap.begin( );
        iter != this->_blockMap.end( ); ++iter )
  {
    delete iter->second;
  }
}

template <class T>
void ObjectPoolManager<T>::AddObjectPool ( TYPE_ID type, MemoryBlock<T>* block )
{
  this->_blockMap[type] = block;
}

template <class T>
ID ObjectPoolManager<T>::CreateObject ( TYPE_ID type )
{
  assert( this->m_Game != 0 );
  return this->_blockMap[type]->Create( this->m_Game );
}

template <class T>
bool ObjectPoolManager<T>::HasObject ( TYPE_ID type, ID ID )
{
  return this->_blockMap[type]->Contains ( ID );
}

template <class T>
T* ObjectPoolManager<T>::GetObject ( TYPE_ID type, ID ID )
{
  return ( T* ) this->_blockMap[type]->Get ( ID );
}

template <class T>
void ObjectPoolManager<T>::CreateBlock ( StompedReality::type_system::TypeInfo* info, int capacity )
{
  this->AddObjectPool ( info->typeID, ObjectPool<T>::CreateObjectPool ( capacity, info ) );
}

template <class T>
MemoryBlock<T>* ObjectPoolManager<T>::GetMemoryBlock ( TYPE_ID type )
{
  return this->_blockMap[type];
}

template <class T>
bool ObjectPoolManager<T>::ParseConfig ( JsonPTR root )
{
  Json::Value rsRoot = root->get( "ComponentSystem", 0 );
  LogCallFunctionInfo( "ObjectPoolManager", "ParseConfig" )
  Json::Value list = rsRoot["components"];
  for ( unsigned index = 0; index < list.size( ); ++index )
  {
    LogCallFunctionSubInfo( "add Component" )
    LogInfoPair( "component", list[index]["component type"].asCString( ) )
    LogInfoPair( "size", list[index]["capacity"].asInt( ) )
    StompedReality::type_system::TypeInfo* info = StompedReality::type_system::TypeSystem::GetTypeByName (
        list[index]["component type"].asCString( ) );
    if ( info == 0 )
    {
      LogCallFunctionSubWarning( "Unknown componnent" );
      continue;
    }
    this->CreateBlock ( info,
                        list[index]["capacity"].asInt( ) );
  }
  return true;
}

typedef DLL_EXPORT ObjectPoolManager<StompedReality::Core::Component> ComponentObjectPool;
}
}
#endif // OBJECTPOOLMANAGER_H
